package com.xingchi.tornado.dict.service;

import com.xingchi.tornado.core.enums.OperateTypeEnum;
import com.xingchi.tornado.core.lock.RedisLock;
import com.xingchi.tornado.dict.constants.DictConstants;
import com.xingchi.tornado.dict.enums.DictSourceEnum;
import com.xingchi.tornado.dict.handler.DictRefreshDeleteGroupPostProcessor;
import com.xingchi.tornado.dict.handler.DictRefreshPostProcessor;
import com.xingchi.tornado.dict.model.DictMetadata;
import com.xingchi.tornado.dict.model.dto.DictGroupAddDTO;
import com.xingchi.tornado.dict.model.dto.DictGroupDTO;
import com.xingchi.tornado.dict.model.dto.DictGroupQueryDTO;
import com.xingchi.tornado.dict.model.dto.DictItemAddDTO;
import com.xingchi.tornado.dict.model.dto.DictItemDTO;
import com.xingchi.tornado.dict.model.dto.DictItemQueryDTO;
import com.xingchi.tornado.utils.DigestUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 数据库存储
 *
 * @author xingchi
 * @date 2024/5/19 20:41
 */
@Slf4j
@RequiredArgsConstructor
public class JdbcDictStorageService implements DictStorageService, InitializingBean {

    private final DictGroupService dictGroupService;
    private final DictItemService dictItemService;
    private final List<DictRefreshDeleteGroupPostProcessor> dictRefreshDeleteGroupPostProcessors;
    private final List<DictRefreshPostProcessor> dictRefreshPostProcessors;
    private final RedisTemplate<String, Object> redisTemplate;
    private BoundHashOperations<String, String, List<DictItemDTO>> cacheOperations;


    /**
     * 存储字典数据
     *
     * @param dictMetadataList 字典数据集合
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void storage(List<DictMetadata> dictMetadataList) {

        if (CollectionUtils.isEmpty(dictMetadataList)) {
            return;
        }

        // 多节点只需一个节点更新即可
        boolean isLock = RedisLock.tryLock(DictConstants.DICT_REDIS_STORAGE_LOCK_KEY);
        try {
            if (isLock) {

                if (!CollectionUtils.isEmpty(dictRefreshPostProcessors)) {
                    for (DictRefreshPostProcessor dictRefreshPostProcessor : dictRefreshPostProcessors) {
                        dictRefreshPostProcessor.preProcessor(dictMetadataList);
                    }
                }

                // 获取所有分组信息
                Set<String>  groupCodes = new HashSet<>();
                for (DictMetadata dictMetadata : dictMetadataList) {

                    groupCodes.add(dictMetadata.getGroup());
                    DictGroupDTO dictGroupDTO = dictGroupService.selectOne(DictGroupQueryDTO.builder().code(dictMetadata.getGroup()).build());
                    if (dictGroupDTO == null) {
                        // 不存在执行添加
                        DictGroupAddDTO dictGroupAddDTO = new DictGroupAddDTO();
                        dictGroupAddDTO.setCode(dictMetadata.getGroup());
                        dictGroupAddDTO.setName(dictMetadata.getName());
                        dictGroupAddDTO.setDesc(dictMetadata.getDesc());
                        dictGroupAddDTO.setSource(DictSourceEnum.ENUM_SCAN.getCode());
                        dictGroupService.addGroup(dictGroupAddDTO);
                    } else {
                        // 存在则判断信息是否和旧数据一致，不一致则更新
                        String sourceGroupDigest = DigestUtils.md5(dictGroupDTO.getCode() + dictGroupDTO.getName() + dictGroupDTO.getDesc());
                        String newGroupDigest = DigestUtils.md5(dictMetadata.getGroup() + dictMetadata.getName() + dictMetadata.getDesc());
                        if (!sourceGroupDigest.equals(newGroupDigest)) {
                            // 更新分组信息
                            DictGroupDTO update = new DictGroupDTO();
                            update.setId(dictGroupDTO.getId());
                            update.setCode(dictMetadata.getGroup());
                            update.setName(dictMetadata.getName());
                            update.setDesc(dictMetadata.getDesc());
                            update.setSource(DictSourceEnum.ENUM_SCAN.getCode());
                            dictGroupService.updateById(update);
                        }

                        // 将原来字典项，全部删除，然后重新添加
                        dictItemService.deleteByGroupCode(dictMetadata.getGroup());
                    }

                    // 添加字典项
                    List<DictItemAddDTO> dictItemAddDTO = this.toDictItemAddDTO(dictMetadata);
                    dictItemService.addItemBatch(dictItemAddDTO);
                }

                // 查询所有扫描的分组信息
                List<DictGroupDTO> dictGroupList = dictGroupService.selectByQuery(DictGroupQueryDTO.builder().source(DictSourceEnum.ENUM_SCAN.getCode()).isPagination(false).build());
                List<String> notExistsGroupCodes = dictGroupList.stream()
                        .map(DictGroupDTO::getCode)
                        .filter(code -> !groupCodes.contains(code))
                        .collect(Collectors.toList());

                if (!CollectionUtils.isEmpty(notExistsGroupCodes)) {
                    if (!CollectionUtils.isEmpty(dictRefreshDeleteGroupPostProcessors)) {
                        for (DictRefreshDeleteGroupPostProcessor dictRefreshDeleteGroupPostProcessor : dictRefreshDeleteGroupPostProcessors) {
                            dictRefreshDeleteGroupPostProcessor.preProcessor(notExistsGroupCodes);
                        }
                    }

                    // 移除旧分组
                    dictGroupService.deleteByGroupCode(notExistsGroupCodes);
                    // 移除分组项
                    dictItemService.deleteByGroupCode(notExistsGroupCodes);

                    if (!CollectionUtils.isEmpty(dictRefreshDeleteGroupPostProcessors)) {
                        for (DictRefreshDeleteGroupPostProcessor dictRefreshDeleteGroupPostProcessor : dictRefreshDeleteGroupPostProcessors) {
                            dictRefreshDeleteGroupPostProcessor.postProcessor(notExistsGroupCodes);
                        }
                    }
                }

                if (!CollectionUtils.isEmpty(dictRefreshPostProcessors)) {
                    for (DictRefreshPostProcessor dictRefreshPostProcessor : dictRefreshPostProcessors) {
                        dictRefreshPostProcessor.postProcessor(dictMetadataList);
                    }
                }
            }
        } finally {
            RedisLock.unlock(DictConstants.DICT_REDIS_STORAGE_LOCK_KEY);
        }
    }

    public List<DictItemAddDTO> toDictItemAddDTO(DictMetadata metadata) {

        return metadata.getItems().stream().map(item -> {
            DictItemAddDTO dictItemAddDTO = new DictItemAddDTO();
            dictItemAddDTO.setGroupCode(metadata.getGroup());
            dictItemAddDTO.setCode(item.getCode());
            dictItemAddDTO.setName(item.getName());
            dictItemAddDTO.setDesc(item.getDesc());
            dictItemAddDTO.setSort(item.getOrder());
            dictItemAddDTO.setVisible(Boolean.TRUE.equals(item.getVisible()) ? 1 : 0);
            return dictItemAddDTO;
        }).collect(Collectors.toList());
    }

    @Override
    public void refreshCache() {

        log.info("开始缓存字典项");
        List<DictItemDTO> dictItems = dictItemService.selectByQuery(DictItemQueryDTO.builder().isPagination(false).build());
        Function<List<DictItemDTO>, List<DictItemDTO>> sortFunction = list -> list.stream().sorted(Comparator.comparingInt(DictItemDTO::getSort)).collect(Collectors.toList());
        Map<String, List<DictItemDTO>> dictGroupItemMappings = dictItems
                .stream()
                .collect(Collectors.groupingBy(DictItemDTO::getGroupCode,
                        // 分组后进行排序
                        Collectors.collectingAndThen(Collectors.toList(), sortFunction)));
        for (Map.Entry<String, List<DictItemDTO>> entry : dictGroupItemMappings.entrySet()) {
            cacheOperations.put(entry.getKey(), entry.getValue());
        }

        // 移除不在其中的子项
        Set<String> allDictKey = cacheOperations.keys();
        if (!CollectionUtils.isEmpty(allDictKey)) {
            for (String key : allDictKey) {
                if (!dictGroupItemMappings.containsKey(key)) {
                    cacheOperations.delete(key);
                }
            }
        }
        log.info("缓存字典项完成");
    }

    @Override
    public Map<String, List<DictItemDTO>> getDictCache() {
        return cacheOperations.entries();
    }

    @Override
    public List<DictItemDTO> getDictCache(String group) {

        List<DictItemDTO> itemList = cacheOperations.get(group);
        if (CollectionUtils.isEmpty(itemList)) {
            // 查数据库
            itemList = dictItemService.selectByQuery(DictItemQueryDTO.builder().groupCode(group).isPagination(false).build());
            if (!CollectionUtils.isEmpty(itemList)) {
                cacheOperations.put(group, itemList);
                return itemList;
            }
        }

        return itemList;
    }

    @Override
    public boolean dictChangeHandler(String groupCode, OperateTypeEnum operateType) {

        switch (operateType) {
            case INSERT:
            case UPDATE:
                // 添加和修改重新构建字典项
                DictGroupDTO dictGroupDTO = dictGroupService.selectOne(DictGroupQueryDTO.builder().code(groupCode).build());
                // 查询条目数据
                List<DictItemDTO> dictItemDTOList = dictItemService.selectByQuery(DictItemQueryDTO.builder().groupCode(groupCode).isPagination(false).build());
                cacheOperations.put(groupCode, dictItemDTOList);
                break;
            case DELETE:
                // 删除对应的缓存key
                cacheOperations.delete(groupCode);
                break;
            default:
                return false;
        }

        return true;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        cacheOperations = redisTemplate.boundHashOps(DictConstants.DICT_REDIS_STORAGE_KEY);
    }
}
